package com.huanzhidadi.single.thread;

import cn.hutool.core.util.RandomUtil;
import com.alibaba.fastjson2.JSON;

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;

/**
 * 一、步骤
 *  1、创建 Callable 接口的实现类，并实现call方法，该call方法会成为线程执行体，且call方法具有返回值，再创建callable对象
 *  2、使用 FutrueTask 类来包装 Callable 对象，该 FutrueTask 封装类Callable的call方法的返回值
 *  3、使用 FutrueTask 对象作为 Thread 的 target 创建并启动新线程
 *  4、使用 FutrueTask 的 get 方法获取执行结束后的返回值
 *
 * 二、实现runnable、callable接口与继承thread类的优缺点
 *  采取 Runnable、Callable 的优势在于——线程类只是实现了Runnable 或 Callable 接口，还可以继承其它类；
 *  在这种方法下，多个线程可以共享一个 target 对象，因此非常适合多个相同线程处理同一份资源的情况，
 *  从而将代码和数据分开，形成清晰的模型，体现了面向对象的编程思想。劣势在于编程复杂度略高。
 *
 */
public class ImplementCallableTest {
    public static void main(String[] args){
        MyThreadImplementCallable callable = new MyThreadImplementCallable();
        List<FutureTask<String>> futureTaskList = new ArrayList<>();
        List<String> results = new ArrayList<>();
        for (int i=0; i<5; i++) {
            FutureTask<String> futureTask = new FutureTask<>(callable);
            Thread thread1 = new Thread(futureTask, "线程" + i);
            thread1.start();
            futureTaskList.add(futureTask);
        }
        for (FutureTask<String> futureTask : futureTaskList) {
            try {
                results.add(futureTask.get());
                //System.out.println(futureTask.get());
            } catch (InterruptedException e) {
                e.printStackTrace();
            } catch (ExecutionException e) {
                e.printStackTrace();
            }
        }
        System.out.println(JSON.toJSONString(results));
        System.out.println( "主线程执行完了" );
    }
}

class MyThreadImplementCallable implements Callable {
    @Override
    public Object call() throws Exception {
        long sleepTime = RandomUtil.randomLong(1000, 3000);
        try {
            Thread.sleep(sleepTime);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(Thread.currentThread().getName() + "休眠" + sleepTime + "ms");
        return Thread.currentThread().getName();
    }
}
